package cn.nkk.hikvision.utils;

import cn.hutool.core.util.StrUtil;
import cn.nkk.hikvision.beans.CameraLogin;
import cn.nkk.hikvision.beans.PlateInfo;
import cn.nkk.hikvision.callBack.DVRMessageCallBackV31;
import cn.nkk.hikvision.properties.HiKProperties;
import cn.nkk.hikvision.sdk.HCNetSDK;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.List;


/**
 * <p>海康威视工具包</p>
 * @author dlj
 *
 */
public final class HkUtils {

    private final static Logger log = LoggerFactory.getLogger(HkUtils.class);
    private static final HCNetSDK hcNetSDK = SpringContextHolder.getBean(HCNetSDK.class);


    /**
     * 加载linux系统类库
     */
    static {
        if (OsSelectUtil.isLinux()) {
            HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
            HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);
            HiKProperties hiKProperties = SpringContextHolder.getBean(HiKProperties.class);
            String strPathCom = hiKProperties.getSdk_path();

            //这里是库的绝对路径，请根据实际情况修改，注意改路径必须有访问权限
            String strPath1 = strPathCom + "/libcrypto.so";
            String strPath2 =  strPathCom + "/libssl.so";

            System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
            ptrByteArray1.write();
            hcNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());

            System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
            ptrByteArray2.write();
            hcNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());


            HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
            System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
            struComPath.write();
            hcNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());
        }
    }
    /**
     * 注册设备
     */
    private static int login(String deviceIp, String username, String password, Short port) {
        hcNetSDK.NET_DVR_Init();
        hcNetSDK.NET_DVR_SetConnectTime(2000,1);
        hcNetSDK.NET_DVR_SetReconnect(10000,true);

        // 设备信息, 输出参数
        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        // 注册设备-登录参数，包括设备地址、登录用户、密码等
        m_strLoginInfo.sDeviceAddress = new byte[hcNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(deviceIp.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, deviceIp.length());
        m_strLoginInfo.sUserName = new byte[hcNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(username.getBytes(), 0, m_strLoginInfo.sUserName, 0, username.length());
        m_strLoginInfo.sPassword = new byte[hcNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(password.getBytes(), 0, m_strLoginInfo.sPassword, 0, password.length());
        m_strLoginInfo.wPort = port;
        // 是否异步登录：0- 否，1- 是
        m_strLoginInfo.bUseAsynLogin = false;
        m_strLoginInfo.write();
        // 设备信息, 输出参数
        int lUserID = hcNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        if (lUserID < 0) {
            log.info("【海康车牌摄像头初始化】注册登录失败: {}", hcNetSDK.NET_DVR_GetErrorMsg(null));
            hcNetSDK.NET_DVR_Cleanup();
            return -1;
        }
        return lUserID;
    }



    public static void plateNumberInit(String deviceIp, String username, String password, Short port){

        int lUserId = login(deviceIp, username, password, port);
        if (lUserId<0){
            return;
        }
        setupAlarmChan(lUserId);

        DVRMessageCallBackV31 callBackV31 = SpringContextHolder.getBean(DVRMessageCallBackV31.class);

        // 启用布防
        setupAlarmChan(lUserId);
        // 设置报警回调函数
        new Thread(() -> {
            try {
                for (; ; ) {
                    hcNetSDK.NET_DVR_SetDVRMessageCallBack_V31(callBackV31, null);
                    Thread.sleep(2000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();


    }






    /**
     * 设置布防
     *
     * @param userId 用户id
     * @return int 返回布防id
     */
    public static int setupAlarmChan(int userId) {
        //布防参数
        HCNetSDK.NET_DVR_SETUPALARM_PARAM m_strAlarmInfo = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
        m_strAlarmInfo.dwSize = m_strAlarmInfo.size();
        m_strAlarmInfo.byLevel = 1; //布防优先级：0- 一等级（高），1- 二等级（中）
        m_strAlarmInfo.byAlarmInfoType = 1; //上传报警信息类型: 0- 老报警信息(NET_DVR_PLATE_RESULT), 1- 新报警信息(NET_ITS_PLATE_RESULT)
        m_strAlarmInfo.byDeployType = 1;
        m_strAlarmInfo.write();
        int nativeLong = hcNetSDK.NET_DVR_SetupAlarmChan_V41(userId, m_strAlarmInfo);
        //如果布防失败返回-1
        if (nativeLong<0){
            log.error("【海康车牌摄像头初始化】启用布防失败: {}", hcNetSDK.NET_DVR_GetLastError());

            hcNetSDK.NET_DVR_Logout(userId);  //注销
            hcNetSDK.NET_DVR_Cleanup(); //释放SDK资源
        }
        log.info("【海康车牌摄像头初始化】布防成功,开始监测车辆：{}",nativeLong);
        return nativeLong;
    }

    /**
     * 撤防
     *
     * @param alarmHandle 布防成功返回的id
     */
    public static void closeAlarmChan(Integer alarmHandle) {
        if (alarmHandle> -1) {
            if (!hcNetSDK.NET_DVR_CloseAlarmChan_V30(alarmHandle)) {
                log.error("【海康车牌摄像头初始化】撤防失败");
            }
        }
        log.info("【海康车牌摄像头初始化】撤防成功");
    }


    /**
     * 注销设备
     *
     * @param userId 用户id
     */
    public static void doLogout(int userId){
        hcNetSDK.NET_DVR_Logout(userId);
        hcNetSDK.NET_DVR_Cleanup();
//        log.info("【海康车牌摄像头初始化】: 设备：{}注销完成",userId);
    }


    /**
     * 侦听端口
     *
     * @param ip   ip
     * @param port 端口
     * @param callback 监听回调
     */
    public static void startListen(String ip, String port, HCNetSDK.FMSGCallBack_V31 callback){
        hcNetSDK.NET_DVR_Init();
        hcNetSDK.NET_DVR_SetConnectTime(2000,1);
        hcNetSDK.NET_DVR_SetReconnect(10000,true);
        /*HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG struNET_DVR_LOCAL_GENERAL_CFG = new HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG();
        struNET_DVR_LOCAL_GENERAL_CFG.byAlarmJsonPictureSeparate = 1;   //设置JSON透传报警数据和图片分离
        struNET_DVR_LOCAL_GENERAL_CFG.write();
        Pointer pStrNET_DVR_LOCAL_GENERAL_CFG = struNET_DVR_LOCAL_GENERAL_CFG.getPointer();
        hcNetSDK.NET_DVR_SetSDKLocalCfg(17, pStrNET_DVR_LOCAL_GENERAL_CFG);*/

        int lListenHandle = hcNetSDK.NET_DVR_StartListen_V30(ip, Short.parseShort(port), callback, null);
        if (lListenHandle == -1) {
            log.error("【海康车牌摄像头初始化】监听失败" + hcNetSDK.NET_DVR_GetLastError());
            throw new RuntimeException("监听失败");
        }
        log.info("【海康车牌摄像头初始化】监听成功！ip:{},port:{}",ip , port);
    }

    /**
     * 停止端口监听
     */
    private static void stopListen(){
        hcNetSDK.NET_DVR_StopListen();
    }





    /**
     * 海康全彩摄像头拍照
     */
    public static String justTakePhoto(String deviceIp, String username, String password, Short port) {

        // 设备信息
        int lUserID = login(deviceIp,username, password, port);
        if (lUserID < 0) {
            return null;
        }
        byte[] bytes = takePhotoWithUserId(lUserID);

        String imagePath = writeFile(bytes, deviceIp.substring(deviceIp.lastIndexOf(".") + 1));
        // 写到本地
        hcNetSDK.NET_DVR_Logout(lUserID);
        hcNetSDK.NET_DVR_Cleanup();
        return imagePath;
    }

    /**
     * 海康SDK手动抓拍并获取车牌
     */
    public static PlateInfo manualSnap(String deviceIp, String username, String password, Short port) {
        // 设备信息
        int lUserID = login(deviceIp,username, password, port);
        HCNetSDK.NET_DVR_PLATE_RESULT struPlateResultInfo = new HCNetSDK.NET_DVR_PLATE_RESULT();
        struPlateResultInfo.pBuffer1 =  new Memory(2 * 1024 * 1024);
        struPlateResultInfo.pBuffer2 =  new Memory( 1024 * 1024);
        HCNetSDK.NET_DVR_MANUALSNAP struInter = new HCNetSDK.NET_DVR_MANUALSNAP();
        struInter.byLaneNo = 1;

        boolean flag = hcNetSDK.NET_DVR_ManualSnap(lUserID, struInter, struPlateResultInfo);

        if (flag){
            String license = null;
            try {

                byte[] byteArray = struPlateResultInfo.pBuffer1.getByteArray(0, struPlateResultInfo.dwPicLen);
                // img1 车辆场景图片(大图)
                String img1 = writeFile(byteArray, deviceIp.substring(deviceIp.lastIndexOf(".") + 1) + "_s");
                byte[] byteArray1 = struPlateResultInfo.pBuffer2.getByteArray(0, struPlateResultInfo.dwPicLen);
                // img2 车牌图片(小图)
                String img2 = writeFile(byteArray1, deviceIp.substring(deviceIp.lastIndexOf(".") + 1) + "_p");
                license = new String(struPlateResultInfo.struPlateInfo.sLicense, "GBK");
                String plateNumber = license.substring(1).trim();
                String byCountry = license.substring(1, 2).trim();
                String byColor = license.substring(0, 1).trim();
                String byType = struPlateResultInfo.byVehicleType + "".trim();
                PlateInfo plateInfo = new PlateInfo();
                plateInfo.setColor(byColor);
                plateInfo.setPlateNumber(plateNumber);
                plateInfo.setCountry(byCountry);
                plateInfo.setType(CarType.getCarType(byType));
                plateInfo.setHost(deviceIp);
                plateInfo.setBigImg(img1);
                if (plateNumber.replace("车牌","").length()!=0){
                    plateInfo.setSmallImg(img2);
                }

                log.info("【海康车牌摄像头回调】车牌号: {}", plateNumber);
                log.info("【海康车牌摄像头回调】车牌省份: {}", byCountry);
                log.info("【海康车牌摄像头回调】车牌颜色: {}", byColor);
                log.info("【海康车牌摄像头回调】车牌类型: {}", byType);
                log.info("【海康车牌摄像头回调】场景图片: {}", img1);
                log.info("【海康车牌摄像头回调】车牌图片: {}", img2);

                hcNetSDK.NET_DVR_Logout(lUserID);
                hcNetSDK.NET_DVR_Cleanup();
                return plateInfo;
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }


        }

        return null;



    }

    /**
     * 写到本地
     */
    private static String writeFile(byte[] bs, String suffix) {
        HiKProperties hiKProperties = SpringContextHolder.getBean(HiKProperties.class);
        String savePath = hiKProperties.getImg_path() +"/"+HkDateUtils.getDate().replace("-","");
        HkFileUtils.checkDirExists(savePath);
        String filename =savePath +"/" + HkDateUtils.getDateTimeString() + "_" + suffix + ".jpg";//, path = property.getImgPath() + filename;
        File file = new File(filename);
        try (FileOutputStream fos = new FileOutputStream(file);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            bos.write(bs);
        } catch (Exception e) {
            log.error("【海康摄像头写入图片失败】", e);
            return null;
        }
        return filename;
    }

    private static byte[] takePhotoWithUserId(int lUserID) {
        // JPEG图像参数
        HCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new HCNetSDK.NET_DVR_JPEGPARA();
        // 设置图片的分辨率
        lpJpegPara.wPicSize = 2;
        // 设置图片质量
        lpJpegPara.wPicQuality = 0;
        // 通道号、输入缓冲区大小
        int channel = 1, dwPicSize = 1024 * 1024;
        // 保存JPEG数据的缓冲区
        Pointer sJpegPicBuffer = new Memory(dwPicSize);
        // 返回图片数据的大小
        IntByReference reference = new IntByReference();
        // 抓拍
        hcNetSDK.NET_DVR_CaptureJPEGPicture_NEW(lUserID, channel, lpJpegPara, sJpegPicBuffer, dwPicSize, reference);
        // 图片大小
        int value = reference.getValue();
        log.info("【海康全彩摄像头】图片大小: {}", value);
        // 图片byte
        return sJpegPicBuffer.getByteArray(0, value);
    }
}
